#pragma once
#include "Decoder.h"
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>

#define MAX_BUFSIZE 128
#define FLOAT_BUFSIZE 64
template <typename T>
class OggDecoder : public Decoder<T>
{
public:
    virtual bool decode(const std::string& filename, Sound<T>& sound)
    {
        OggVorbis_File vf;

        if (ov_fopen(filename.c_str(), &vf) < 0)
        {
            printf("open file failed");
            return false;
        }

        vorbis_info * vi = ov_info(&vf, -1);
        sound._hz = vi->rate;
        int channels = vi->channels;

        int current_section = 0;
        sound._pcm.resize(channels);

        char pcm[MAX_BUFSIZE];
        int typeSize = sizeof(T);
        int channelLength = MAX_BUFSIZE / channels / typeSize;

        int eof = 0;
        while (!eof) 
        {
            long ret = ov_read(&vf, pcm, sizeof(pcm), 0, 2, 1, &current_section);

            if (ret == 0)
            {
                eof = 1;
            }
            else if (ret < 0)
            {
                if (ret == OV_EBADLINK) {
                    printf("Corrupt bitstream section! Exiting.\n");
                    ov_clear(&vf);
                    return false;
                }
            }
            else 
            {
                for (int i = 0; i < channelLength; i++)
                {
                    for (int j = 0; j < channels; j++)
                    {
                        sound._pcm[j].push_back( *(T*)(pcm + typeSize * (i*channels + j)) );
                    }
                }
            }
        }
        
        ov_clear(&vf);

        return true;
    }
};

template <>
class OggDecoder<float> : public Decoder<float>
{
public:
    virtual bool decode(const std::string& filename, Sound<float>& sound)
    {
        OggVorbis_File vf;

        if (ov_fopen(filename.c_str(), &vf) < 0)
        {
            printf("open file failed");
            return false;
        }

        vorbis_info * vi = ov_info(&vf, -1);
        sound._hz = vi->rate;
        int channels = vi->channels;

        float** pcm;
        int current_section = 0;

        sound._pcm.resize(channels);
        int eof = 0;
        while (!eof)
        {
            long ret = ov_read_float(&vf, &pcm, FLOAT_BUFSIZE, &current_section);

            if (ret == 0)
            {
                eof = 1;
            }
            else if (ret < 0)
            {
                if (ret == OV_EBADLINK) {
                    printf("Corrupt bitstream section! Exiting.\n");
                    ov_clear(&vf);
                    return false;
                }
            }
            else
            {
                for (int ichannel = 0; ichannel < channels; ichannel++)
                {
                    for (size_t i = 0; i < FLOAT_BUFSIZE; i++)
                    {
                        sound._pcm[ichannel].push_back(pcm[ichannel][i]);
                    }
                }
            }
        }

        ov_clear(&vf);

        return true;
    }
};
